﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Text;

namespace SpreadDesigner
{
  internal abstract class FileDialogBase : IDisposable
  {
    protected Control ExtendedControl;
    private static bool isVistaOS = (Environment.OSVersion.Version.Major >= 6);

    public virtual void Dispose()
    {
      this.Dialog.Dispose();
      this.DialogNative.Dispose();
    }

    protected bool IsVistaDialog()
    {
      return (this.Dialog.AutoUpgradeEnabled && isVistaOS);
    }

    public DialogResult ShowDialog(IWin32Window owner)
    {
      DialogResult cancel = DialogResult.Cancel;
      OwnerNative native = new OwnerNative(this);
      Win32.SetWindowPos(native.Handle, IntPtr.Zero, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_HIDEWINDOW | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE);
      native.AssignHandle(owner.Handle);
      native.WaitForActivate = true;
      try
      {
        cancel = this.Dialog.ShowDialog(owner);
      }
      finally
      {
        native.Dispose();
        native = null;
      }
      return cancel;
    }

    public abstract FileDialog Dialog { get; }

    protected abstract FileDialogNative DialogNative { get; }

    public abstract class FileDialogNative : NativeWindow, IDisposable
    {
      protected NativeButton controlToSize;
      private bool ignored = true;
      internal bool isClosing;
      protected bool isVistaDialog;
      protected IntPtr nextControlHandle;
      protected internal IntPtr openDialogHandle;
      private bool resized = false;
      protected Control sourceControl;
      protected const SetWindowPosFlags UFLAGSHIDE = (SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_HIDEWINDOW | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE);
      protected const SetWindowPosFlags UFLAGSMOVE = (SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOSIZE);
      protected const SetWindowPosFlags UFLAGSSIZE = (SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE);
      protected const SetWindowPosFlags UFLAGSSIZEEX = (SetWindowPosFlags.SWP_ASYNCWINDOWPOS | SetWindowPosFlags.SWP_DEFERERASE | SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE);
      protected const SetWindowPosFlags UFLAGSZORDER = (SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE);

      public FileDialogNative(Control sourceControl, bool isVistaDialog)
      {
        this.sourceControl = sourceControl;
        this.isVistaDialog = isVistaDialog;
      }

      public virtual void Dispose()
      {
        if (this.controlToSize != null)
        {
          this.controlToSize.Dispose();
          this.controlToSize = null;
        }
        this.ReleaseHandle();
        if (this.sourceControl is IDisposable)
        {
          this.sourceControl.Dispose();
        }
      }

      protected abstract bool FileDialogEnumWindowCallBack(IntPtr hwnd, int lParam);
      internal void InitControls()
      {
        this.PopulateWindowsHandlers();
        Win32.SetParent(this.sourceControl.Handle, this.openDialogHandle);
        Win32.SetWindowPos(this.sourceControl.Handle, this.nextControlHandle, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE);
      }

      private void PopulateWindowsHandlers()
      {
        Win32.EnumChildWindows(this.openDialogHandle, new Win32.EnumWindowsCallBack(this.FileDialogEnumWindowCallBack), 0);
        RECT rect = new RECT();
        Win32.GetClientRect(this.openDialogHandle, ref rect);
      }

      protected internal abstract void SizingAndMovingAddedControl();
      protected override void WndProc(ref Message m)
      {
        int msg = m.Msg;
        switch (msg)
        {
          case 0x47:
            this.SizingAndMovingAddedControl();
            goto Label_024D;

          case 0x18:
            Win32.SetWindowPos(this.sourceControl.Handle, this.nextControlHandle, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE);
            goto Label_024D;

          case 70:
            if ((!this.isVistaDialog && !this.isClosing) && !this.resized)
            {
              WINDOWPOS structure = (WINDOWPOS)Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));
              if ((structure.flags != 0L) && ((structure.flags & 1L) != 1L))
              {
                this.resized = true;
                structure.cy += (int)Math.Round((double)(1.3 * this.sourceControl.Height));
                Marshal.StructureToPtr(structure, m.LParam, true);
                this.SizingAndMovingAddedControl();
              }
            }
            goto Label_024D;

          case 0x214:
            this.SizingAndMovingAddedControl();
            goto Label_024D;

          case 0x282:
            if (m.WParam == ((IntPtr)1))
            {
              this.isClosing = true;
              this.ignored = false;
            }
            goto Label_024D;

          case 0x111:
            switch (Win32.GetDlgCtrlID(m.LParam))
            {
              case 1:
              case 2:
                this.ignored = false;
                break;
            }
            goto Label_024D;
        }
        if (((msg == 0x86) && this.ignored) && ((m.WParam != ((IntPtr)1)) && (m.LParam != Win32.GetParent(this.Handle))))
        {
          IntPtr ptr3 = new IntPtr(1);
          m.Result = ptr3;
          return;
        }
      Label_024D:
        base.WndProc(ref m);
        if ((310 == m.Msg) && (Win32.GetWindow(this.sourceControl.Handle, 3) != this.nextControlHandle))
        {
          Win32.SetWindowPos(this.sourceControl.Handle, this.nextControlHandle, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE);
        }
      }
    }

    private class OwnerNative : NativeWindow, IDisposable
    {
      private FileDialogBase fileDialogBase = null;
      private FileDialogBase.FileDialogNative nativeDialog = null;
      private bool waitActivate = false;

      public OwnerNative(FileDialogBase fileDialogBase)
      {
        this.fileDialogBase = fileDialogBase;
      }

      public void Dispose()
      {
        this.ReleaseHandle();
      }

      protected override void WndProc(ref Message m)
      {
        if (this.waitActivate && (m.Msg == 6))
        {
          this.waitActivate = false;
          this.nativeDialog = this.fileDialogBase.DialogNative;
          this.nativeDialog.openDialogHandle = m.LParam;
          this.nativeDialog.AssignHandle(m.LParam);
          this.nativeDialog.InitControls();
        }
        base.WndProc(ref m);
      }

      public bool WaitForActivate
      {
        get
        {
          return this.waitActivate;
        }
        set
        {
          this.waitActivate = value;
        }
      }
    }
  }
  internal class Win32
  {
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr BeginDeferWindowPos(int nNumWindows);
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
    public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr ChildWindowFromPoint(IntPtr hWndParent, PointStruct point);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr ChildWindowFromPointEx(IntPtr hParent, PointStruct pt, ChildFromPointFlags flags);
    [DllImport("user32.dll")]
    public static extern IntPtr ClientToScreen(IntPtr hwnd, ref PointStruct lpPoint);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int Width, int Height, SetWindowPosFlags flags);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
    [DllImport("user32.Dll")]
    public static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowsCallBack lpEnumFunc, int lParam);
    [DllImport("user32.Dll")]
    public static extern bool EnumWindows(EnumWindowsCallBack lpEnumFunc, int lParam);
    [DllImport("user32.dll", EntryPoint = "FindWindowExA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int GET_X_LPARAM(IntPtr lParam);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int GET_Y_LPARAM(IntPtr lParam);
    [DllImport("User32.Dll")]
    public static extern void GetClassName(IntPtr hWnd, StringBuilder param, int length);
    [DllImport("user32.dll")]
    public static extern bool GetClientRect(IntPtr hwnd, ref RECT rect);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool GetCursorPos(ref PointStruct point);
    [DllImport("User32.Dll")]
    public static extern int GetDlgCtrlID(IntPtr hWndCtl);
    [DllImport("user32")]
    public static extern int GetDoubleClickTime();
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr GetFocus();
    [DllImport("user32")]
    public static extern int GetKeyboardState(byte[] pbKeyState);
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
    public static extern short GetKeyState(int vKey);
    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern int GetMessagePos();
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr GetParent(IntPtr hWnd);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool GetWindowInfo(IntPtr hwnd, out WINDOWINFO pwi);
    [DllImport("user32.dll")]
    public static extern bool GetWindowRect(IntPtr hwnd, ref RECT rect);
    [DllImport("User32.Dll")]
    public static extern void GetWindowText(IntPtr hWnd, StringBuilder param, int length);
    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern bool InvalidateRect(IntPtr hWnd, COMRECT rect, bool erase);
    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern int MapWindowPoints(IntPtr hWnd, IntPtr hWndTo, ref PointStruct pt, int cPoints);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int PostMessage(IntPtr hWnd, int msg, int wParam, int lParam);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern bool ReleaseCapture();
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
    public static extern int ScreenToClient(IntPtr hWnd, [In, Out] Point pt);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, StringBuilder param);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, char[] chars);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SetActiveWindow(IntPtr hWnd);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SetCapture(IntPtr hWnd);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SetFocus(IntPtr hWnd);
    [DllImport("user32.dll")]
    public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int Width, int Height, SetWindowPosFlags flags);
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
    [DllImport("user32")]
    public static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState, byte[] lpwTransKey, int fuState);
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int UnhookWindowsHookEx(int idHook);
    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern bool UpdateWindow(IntPtr hWnd);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr WindowFromPoint(int xPoint, int yPoint);

    public delegate bool EnumWindowsCallBack(IntPtr hWnd, int lParam);
  }

  internal class NativeButton : NativeWindow, IDisposable
  {
    private FileDialogBase.FileDialogNative parent;

    public NativeButton(FileDialogBase.FileDialogNative parent)
    {
      this.parent = parent;
    }

    public void Dispose()
    {
      this.ReleaseHandle();
      this.parent = null;
    }

    protected override void WndProc(ref Message m)
    {
      if (m.Msg == 3)
      {
        this.parent.SizingAndMovingAddedControl();
      }
      base.WndProc(ref m);
    }
  }


  [StructLayout(LayoutKind.Sequential)]
  internal struct PointStruct
  {
    public int x;
    public int y;
    public PointStruct(int x, int y)
    {
      this = new PointStruct();
      this.x = x;
      this.y = y;
    }

    public PointStruct(PointStruct pointStruct)
    {
      this = new PointStruct();
      this.x = pointStruct.x;
      this.y = pointStruct.y;
    }
  }

  internal enum ChildFromPointFlags
  {
    CWP_ALL = 0,
    CWP_SKIPDISABLED = 2,
    CWP_SKIPINVISIBLE = 1,
    CWP_SKIPTRANSPARENT = 4
  }

  internal enum SetWindowPosFlags
  {
    SWP_ASYNCWINDOWPOS = 0x4000,
    SWP_DEFERERASE = 0x2000,
    SWP_DRAWFRAME = 0x20,
    SWP_FRAMECHANGED = 0x20,
    SWP_HIDEWINDOW = 0x80,
    SWP_NOACTIVATE = 0x10,
    SWP_NOCOPYBITS = 0x100,
    SWP_NOMOVE = 2,
    SWP_NOOWNERZORDER = 0x200,
    SWP_NOREDRAW = 8,
    SWP_NOREPOSITION = 0x200,
    SWP_NOSENDCHANGING = 0x400,
    SWP_NOSIZE = 1,
    SWP_NOZORDER = 4,
    SWP_SHOWWINDOW = 0x40
  }

  [StructLayout(LayoutKind.Sequential)]
  internal struct RECT
  {
    public uint left;
    public uint top;
    public uint right;
    public uint bottom;
    public PointStruct Location
    {
      get
      {
        return new PointStruct((int)this.left, (int)this.top);
      }
      set
      {
        this.right -= this.left - ((uint)value.x);
        this.bottom -= this.bottom - ((uint)value.y);
        this.left = (uint)value.x;
        this.top = (uint)value.y;
      }
    }
    public uint Width
    {
      get
      {
        return (this.right - this.left);
      }
      set
      {
        this.right = this.left + value;
      }
    }
    public uint Height
    {
      get
      {
        return (this.bottom - this.top);
      }
      set
      {
        this.bottom = this.top + value;
      }
    }
    public override string ToString()
    {
      return string.Concat(new object[] { this.left, ":", this.top, ":", this.right, ":", this.bottom });
    }
  }

  [StructLayout(LayoutKind.Sequential)]
  internal struct WINDOWINFO
  {
    public uint cbSize;
    public RECT rcWindow;
    public RECT rcClient;
    public uint dwStyle;
    public uint dwExStyle;
    public uint dwWindowStatus;
    public uint cxWindowBorders;
    public uint cyWindowBorders;
    public ushort atomWindowType;
    public ushort wCreatorVersion;
  }

  [StructLayout(LayoutKind.Sequential)]
  internal struct WINDOWPOS
  {
    public IntPtr hwnd;
    public IntPtr hwndAfter;
    public int x;
    public int y;
    public int cx;
    public int cy;
    public uint flags;
    public override string ToString()
    {
      return string.Concat(new object[] { this.x, ":", this.y, ":", this.cx, ":", this.cy, ":", ((SWP_Flags)((int)this.flags)).ToString() });
    }
  }

  internal delegate int HookProc(int nCode, int wParam, IntPtr lParam);

  [StructLayout(LayoutKind.Sequential)]
  internal class COMRECT
  {
    public int left;
    public int top;
    public int right;
    public int bottom;
    public COMRECT()
    {
    }

    public COMRECT(Rectangle r)
    {
      this.left = r.X;
      this.top = r.Y;
      this.right = r.Right;
      this.bottom = r.Bottom;
    }

    public COMRECT(int left, int top, int right, int bottom)
    {
      this.left = left;
      this.top = top;
      this.right = right;
      this.bottom = bottom;
    }

    public static COMRECT FromXYWH(int x, int y, int width, int height)
    {
      return new COMRECT(x, y, x + width, y + height);
    }

    public override string ToString()
    {
      return string.Concat(new object[] { "Left = ", this.left, " Top ", this.top, " Right = ", this.right, " Bottom = ", this.bottom });
    }
  }

  [Flags]
  internal enum SWP_Flags
  {
    SWP_DRAWFRAME = 0x20,
    SWP_FRAMECHANGED = 0x20,
    SWP_HIDEWINDOW = 0x80,
    SWP_NOACTIVATE = 0x10,
    SWP_NOMOVE = 2,
    SWP_NOOWNERZORDER = 0x200,
    SWP_NOREPOSITION = 0x200,
    SWP_NOSIZE = 1,
    SWP_NOZORDER = 4,
    SWP_SHOWWINDOW = 0x40
  }


}


